package com.captjack.common.util.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程池工厂类，应用此工具生成所需的线程池，不建议直接使用Executors类中的静态方法直接创建线程池，
 * 容易引起OOM异常
 *
 * @author : Capt Jack
 * @date : Create in 2017/12/21 20:32
 * Description:
 * package: com.cc.thread
 */
public class ThreadPoolFactory {

    /**
     * 线程池拒绝策略1：丢弃任务并抛出RejectedExecutionException异常。 (默认)
     */
    private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER_ABORT_POLICY = new ThreadPoolExecutor.AbortPolicy();

    /**
     * 线程池拒绝策略2：丢弃任务，但是不抛出异常。
     */
    private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER_DISCARD_OLDEST_POLICY = new ThreadPoolExecutor.DiscardOldestPolicy();

    /**
     * 线程池拒绝策略3：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
     */
    private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER_DISCARD_POLICY = new ThreadPoolExecutor.DiscardPolicy();

    /**
     * 线程池拒绝策略4：由调用线程处理该任务
     */
    private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER_CALLER_RUNS_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();

    /**
     * 创建线程池时，阻塞队列的默认长度
     */
    private static final int DEFAULT_QUEUE_SIZE = 100;

    /**
     * 默认使用的线程工厂类
     */
    private static final ThreadFactory DEFAULT_THREAD_FACTORY = Executors.defaultThreadFactory();

    /**
     * 缓存线程池最大数，超过的话抛出异常
     */
    private static final int CACHE_THREAD_POLL_MAX_SIZE = 2000;

    /**
     * 创建固定大小的线程池，可以延迟或定时的执行任务。
     *
     * @return 定时线程池
     */
    public static ScheduledExecutorService newScheduledThreadPool(int initSize, String prefix) {
        return new ScheduledThreadPoolExecutor(initSize, getThreadFactory(prefix), REJECTED_EXECUTION_HANDLER_ABORT_POLICY);
    }

    /**
     * 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，
     * 那么就会回收部分空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。
     * 此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。
     *
     * @return 可缓存线程池
     */
    public static ExecutorService newCachedThreadPool(String prefix) {
        return new ThreadPoolExecutor(0, CACHE_THREAD_POLL_MAX_SIZE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), getThreadFactory(prefix), REJECTED_EXECUTION_HANDLER_ABORT_POLICY);
    }

    /**
     * 创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。
     * 线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
     *
     * @param initSize 初始化线程池容量大小
     * @return 指定长度线程池
     */
    public static ExecutorService newFixedThreadPool(int initSize, String prefix) {
        return new ThreadPoolExecutor(initSize, initSize, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(DEFAULT_QUEUE_SIZE), getThreadFactory(prefix), REJECTED_EXECUTION_HANDLER_ABORT_POLICY);
    }

    /**
     * 创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。
     * 如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。
     * 此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
     *
     * @return 单线程池
     */
    public static ExecutorService newSingleThreadExecutor(String prefix) {
        return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(DEFAULT_QUEUE_SIZE), getThreadFactory(prefix), REJECTED_EXECUTION_HANDLER_ABORT_POLICY);
    }

    /**
     * 获取默认线程工厂
     *
     * @param prefix 线程名前缀
     * @return 线程工厂
     */
    private static ThreadFactory getThreadFactory(final String prefix) {
        return new ThreadFactory() {
            AtomicInteger atomicInteger = new AtomicInteger();

            @Override
            public Thread newThread(Runnable runnable) {
                Thread thread = new Thread(runnable);
                // 默认名
                thread.setName(prefix + atomicInteger.getAndIncrement());
                return thread;
            }
        };
    }

}
